{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Data của bài này mình mượn từ [nguồn này](https://github.com/animesh-agarwal/Machine-Learning/tree/master/LogisticRegression/data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Dùng pandas để hiển thị data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ta quy định trước\n",
    "- Những điểm có nhãn là 0 nghĩa là sẽ có màu đỏ\n",
    "- Những điểm có nhãn là 1 nghĩa là sẽ có màu xanh"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv(\"./marks.txt\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_to_display = data.iloc[:, :-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Lấy nhãn ra khỏi data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_to_display = data.iloc[:, -1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reds = data.loc[y_to_display == 0] # Reds: 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Lấy dữ liệu có màu xanh nhãn là 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "blues = data.loc[y_to_display == 1] # Blues: 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Lấy dữ liệu màu đỏ có nhãn là 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "plt.scatter(blues.iloc[:, 0], blues.iloc[:, 1], s=10, label='Blue')\n",
    "plt.scatter(reds.iloc[:, 0], reds.iloc[:, 1], s=10, label='Red')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Sử dụng numpy để tách dữ liệu**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy import genfromtxt\n",
    "my_data = genfromtxt('./marks.txt', delimiter=',')\n",
    "\n",
    "X, y = my_data[:, :-1], my_data[:, -1]\n",
    "\n",
    "X = np.concatenate((X, np.ones((X.shape[0], 1))), axis=1)\n",
    "y = np.reshape(y, (y.shape[0], 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Chú ý:** Chia data thành 2 bộ: một bộ dành cho train và một bộ dành để test độ chính xác sau khi train. Thực tế sẽ có thêm một bộ validation test để kiểm soát độ chính xác của mô hình trong khi train nhưng để đơn giản dễ hiểu hơn ở bài này tôi chỉ dùng duy nhất 2 bộ là train và test."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 95 điểm dữ liệu dành để train\n",
    "train_x = X[:95] # Shape(95, 3)\n",
    "train_y = y[:95] # Shape(95, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5 điểm dữ liệu dành cho test\n",
    "test_x = X[95:] # Shape(5, 3)\n",
    "test_y = y[95:] # Shape(5, 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Mục tiêu:** Ta phải tìm đường có thể phân cách 2 vùng dữ liệu màu xanh và màu đỏ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Hàm Sigmoid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/070fae25c3784621470c279cabe7c08d4662f6f5/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c696e6c696e652673706163653b5c6c617267652673706163653b7a2673706163653b3d2673706163653b5c74686574615e5478\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cal_z(x, theta):\n",
    "    \"\"\"\n",
    "    Parameters\n",
    "    ----------\n",
    "    x shape: (95, 3) \n",
    "    theta shape: (3, 1)\n",
    "    Returns\n",
    "    ----------\n",
    "    z shape (95, 1)\n",
    "    \"\"\"\n",
    "    return np.dot(x, theta)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/83c5dcf289d3defb7f56c9c5bb931eb132ddd56b/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c696e6c696e652673706163653b5c6c617267652673706163653b685f7b5c74686574617d2878292673706163653b3d2673706163653b5c667261637b317d7b312673706163653b2b2673706163653b655e7b2d7a7d7d2673706163653b3d2673706163653b5c667261637b317d7b312673706163653b2b2673706163653b655e7b2d7b5c74686574617d5e54787d7d\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sigmoid(z):\n",
    "    \"\"\"\n",
    "    Parameters\n",
    "    z shape: (95, 1)\n",
    "    Returns\n",
    "    output shape: (95, 1)\n",
    "    \"\"\"\n",
    "    return 1 / (1 + np.exp(-z))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cal_h_theta(x, theta):\n",
    "    \"\"\"\n",
    "    Parameters\n",
    "    ----------\n",
    "    x shape: (95, 3)\n",
    "    theta shape: (3, 1)\n",
    "    Returns\n",
    "    ----------\n",
    "    output shape: (95, 1)\n",
    "    \"\"\"\n",
    "    z = cal_z(x, theta)\n",
    "    return sigmoid(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/388a83884ff869439d8527ee738f903ec9bb1ea2/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c736d616c6c2673706163653b4a285c7468657461292673706163653b3d2673706163653b5c2673706163653b5c667261637b2d317d7b6d7d2673706163653b5c73756d5f7b697d5e7b6d7d2673706163653b795f692673706163653b6c6f6728685f5c746865746128785f6929292673706163653b2b2673706163653b28312d795f69296c6f6728312d685f5c746865746128785f692929\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loss_function(x, y, theta):\n",
    "    \"\"\"\n",
    "    Parameters\n",
    "    ----------\n",
    "    x shape: (95, 3)\n",
    "    y shape: (95, 1)\n",
    "    theta shape: (3, 1)\n",
    "    Returns\n",
    "    ----------\n",
    "    loss: float\n",
    "    \"\"\"\n",
    "    h_theta = cal_h_theta(x, theta)\n",
    "    loss = -(y*np.log(h_theta) + (1-y)*np.log(1-h_theta))\n",
    "    return np.mean(loss)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/ae66383a1f3c6c5f71a6f4164326e5eff4c26ff4/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c667261637b5c7061727469616c2673706163653b4a285c7468657461297d7b5c7061727469616c2673706163653b5c74686574615f697d2673706163653b3d2673706163653b5c667261637b317d7b6d7d2673706163653b5c73756d5f7b693d317d5e7b6d7d28685f5c746865746128785e69292673706163653b2d2673706163653b795e6929785f6a5e69\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gradient(x, y, theta):\n",
    "    \"\"\"\n",
    "    Parameters\n",
    "    ----------\n",
    "    x shape: (95, 3)\n",
    "    y shape: (95, 1)\n",
    "    theta shape: (3, 1)\n",
    "    Returns\n",
    "    ----------\n",
    "    grad shape: (3, 1)\n",
    "    \"\"\"\n",
    "    h_theta = cal_h_theta(x, theta)\n",
    "    grad = np.mean(x*(h_theta-y), axis=0)\n",
    "    return grad.reshape((grad.shape[0], 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/818b5bd919e070ccbba779390664cf8915fe1a71/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c696e6c696e652673706163653b5c6c617267652673706163653b5c74686574615f312673706163653b3d2673706163653b5c74686574615f302673706163653b2d2673706163653b5c616c7068612673706163653b5c626967747269616e676c65646f776e2673706163653b4a285c74686574615f3029\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Chuẩn hoá dữ liệu\n",
    "# Quá trình này làm hài hoà mức độ ảnh hưởng của các biến với đầu ta. Ta sẽ nói kỹ về quá trình này trong các bài sau.\n",
    "def normalize_data(train_x, test_x):\n",
    "    train_mean = np.mean(train_x, axis=(0,1), dtype=np.float64, keepdims=True)\n",
    "    train_std = np.std(train_x, axis=(0,1), dtype=np.float64, keepdims=True)\n",
    "    \n",
    "    train_x = (train_x-train_mean)/train_std\n",
    "    test_x = (test_x-train_mean)/train_std\n",
    "    return train_x, test_x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "theta = np.zeros((X.shape[1], 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "theta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Chú ý:** Một số hyperparameters luôn cần có đó là số lượt train và tốc độc học"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "num_epoches = 10000\n",
    "learning_rate = 0.1\n",
    "\n",
    "\n",
    "train_x, test_x = normalize_data(train_x, test_x)\n",
    "\n",
    "for e in range(num_epoches):\n",
    "    # Tính gradient\n",
    "    grad = gradient(train_x, train_y, theta)\n",
    "    \n",
    "    # Update theta\n",
    "    theta = theta - learning_rate * grad\n",
    "    total_loss = loss_function(train_x, train_y, theta)\n",
    "    \n",
    "    \n",
    "    if e % 1000 == 0:\n",
    "        print(total_loss)\n",
    "    \n",
    "print('Training finished')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://miro.medium.com/max/1600/1*PkEl-8DBQa-xEft_tacXLQ.gif\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hình ảnh chỉ mang tính chất minh hoạ vì mình chưa có đủ thời gian làm animation :D. Nhưng quá trình training của chúng ta sẽ tương tự như vậy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "X.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "theta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Tiến hành dự đoán"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ta cho output của hàm sigmoid ra lớn hơn hoặc bằng 0.5 sẽ là nhãn 1 và nhỏ hơn 0.5 sẽ là nhãn 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "predicted_classes = (cal_h_theta(test_x, theta) >= 0.5).astype(int) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "accuracy = np.mean(predicted_classes == test_y) * 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vậy là khả năng dự đoán những dự liệu mới của chúng ta là:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "accuracy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Biểu diễn đường thẳng phân cách"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://camo.githubusercontent.com/070fae25c3784621470c279cabe7c08d4662f6f5/68747470733a2f2f6c617465782e636f6465636f67732e636f6d2f7376672e6c617465783f5c696e6c696e652673706163653b5c6c617267652673706163653b7a2673706163653b3d2673706163653b5c74686574615e5478\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chú ý z chính là đường thẳng phân loại data của chúng ta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://miro.medium.com/max/1280/1*bcWJyX3iAIr0MGNIxUnQxg.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Link ảnh từ [đây](https://towardsdatascience.com/building-a-logistic-regression-in-python-301d27367c24)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
